package pl.mw.cmd.internal.interpreter;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;

import pl.mw.cmd.executor.CommandExecutor;
import pl.mw.cmd.executor.ResultWriter;
import pl.mw.cmd.telnetserver.MessageReceiver;
import pl.mw.cmd.telnetserver.ReplaySender;

/**
 * Interpreter komend.
 */
public class CommandInterpreter implements MessageReceiver {
	
	/** Kontekst komponentu. */
	private ComponentContext ctx;
	
	/** Mapa nazwa komendy - wykonawca komendy. */
	private Map<String, ServiceReference> executorMap;
	
	/**
	 * 
	 */
	public CommandInterpreter() {
		this.executorMap = new ConcurrentHashMap<String, ServiceReference>();
		System.out.println("[pl.mw.cmd.interpreter] [CommandInterpreter] Component created.");
	}

	/**
	 * Startuje parser.
	 */
	public void start(ComponentContext pCtx) {
		this.ctx = pCtx;
		System.out.println("[pl.mw.cmd.interpreter] [CommandInterpreter] Component activated.");
	}

	/**
	 * Zatrzymuje parser.
	 */
	public void stop() {
		System.out.println("[pl.mw.cmd.interpreter] [CommandInterpreter] Component deactivated.");
	}
	
	/**
	 * Dodaje wykonawce komendy.
	 * @param commandExecutor wykonawca komendy
	 * @param componentProps wlasciwosci komponentu wykonawcy
	 */
	public void addCommandExecutor(ServiceReference commandExecutor) {
		String commandName = (String) commandExecutor.getProperty("commandName");
		this.executorMap.put(commandName, commandExecutor);
		System.out.println("[pl.mw.cmd.interpreter] [CommandInterpreter] executor (" + commandName + ") bound.");
	}
	
	/**
	 * Usuwa wykonawce komendy.
	 * @param commandExecutor wykonawca komendy
	 * @param componentProps wlasciwosci komponentu wykonawcy
	 */
	public void removeCommandExecutor(ServiceReference commandExecutor) {
		String commandName = (String) commandExecutor.getProperty("commandName");
		this.executorMap.remove(commandName);
		System.out.println("[pl.mw.cmd.interpreter] [CommandInterpreter] executor (" + commandName + ") unbound.");
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see MessageReceiver#messageReceived(String, ReplaySender)
	 */
	@Override
	public void messageReceived(String message, ReplaySender replayWriter) {
		
		String[] commandAndArgs = message.split("\\s");
		if (commandAndArgs.length < 1) {
			replayWriter.send("\r\n> ");
		} else {
			String commandName = commandAndArgs[0];
			ServiceReference reference = this.executorMap.get(commandName);
			if (reference != null) {
				String[] args = getArgs(commandAndArgs);
				ResultWriter resultWriter = new ResultWriterImpl(replayWriter);
				getCommandExecutor(reference).execute(args, resultWriter);
			} else {
				replayWriter.send("Nieznane polecenie.\r\n> ");
			}
		}
	}
	
	/**
	 * Pobiera obiekt {@link MessageReceiver}
	 * @return obiekt {@link MessageReceiver}
	 */
	public CommandExecutor getCommandExecutor(ServiceReference reference) {
		return (CommandExecutor) this.ctx.locateService("CommandExecutor", reference);
	}

	/**
	 * Pobiera argumenty z tablicy zawierajacej komende i argumenty.
	 * @param commandAndArgs komenda i argumenty
	 * @return argumenty
	 */
	private String[] getArgs(String[] commandAndArgs) {
		if (commandAndArgs.length > 1) {
			return Arrays.copyOfRange(commandAndArgs, 1, commandAndArgs.length);
		} else {
			return new String[0];
		}
	}
	
	/**
	 * Implementacja {@link ResultWriter}.
	 */
	private class ResultWriterImpl implements ResultWriter {
		
		/** Umozliwia wyslanie odpowiedzi. */
		private ReplaySender replaySender;

		/**
		 * @param replaySender umozliwia wyslanie odpowiedzi
		 */
		public ResultWriterImpl(ReplaySender replaySender) {
			this.replaySender = replaySender;
		}

		/**
		 * {@inheritDoc}
		 * 
		 * @see ResultWriter#write(String)
		 */
		@Override
		public void write(String result) {
			this.replaySender.send(result);
		}
	}

}
